package com.bupt.godxian.charging.service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bupt.godxian.charging.entity.*;
import com.bupt.godxian.charging.mapper.*;
import com.bupt.godxian.charging.util.Timer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
public class ScheduleService {

    @Autowired
    private ChargeRequestMapper chargeRequestMapper;

    @Autowired
    private ChargeAreaMapper chargeAreaMapper;

    @Autowired
    private ChargeWaitAreaMapper chargeWaitAreaMapper;

    @Autowired
    private WaitAreaMapper waitAreaMapper;

    @Autowired
    private ChargerMapper chargerMapper;

    @Autowired
    private WaitQueueMapper waitQueueMapper;

    @Autowired
    private ChargeRecordMapper chargeRecordMapper;

    @Transactional
    public void schedule(int scheduleType, Long requestId, String type, Long errChargerId, boolean mustSch, boolean error) {
        // 有人完成充电或取消充电
        if (scheduleType == 1) {
            //获取最近的一次请求
            ChargeRequest last = chargeRequestMapper.getChargeRequestById(requestId);
            String chargeMode = last.getChargeMode();
            String pileId = last.getChargePileId();

            List<String> chargeList = chargeAreaMapper.selectBypileId(pileId);
            List<String> chargeWaitList = chargeWaitAreaMapper.selectChargeRequestIdsByChargeMode(chargeMode);
            List<String> waitList = waitAreaMapper.selectWaitRequestIdsByChargeMode(chargeMode);

            ChargeRequest chargeDone = chargeRequestMapper.selectByIdone(requestId);
            if (chargeDone.getState() == 1 || chargeDone.getState() == 4) {
                return;
            }


            // 让该充电桩第二辆车充电
            chargeList.remove(String.valueOf(chargeDone.getId()));

            // 检查充电区的等候区、等候区，有则叫号进入充电区
            if (chargeWaitList.size() > 0) {
                requestId = Long.valueOf(chargeWaitList.remove(0));
                chargeList.add(String.valueOf(requestId));
                chargeWaitAreaMapper.deleteById(requestId);
            } else if (waitList.size() > 0) {
                requestId = Long.valueOf(waitList.remove(0));
                chargeList.add(String.valueOf(requestId));
                //   waitAreaMapper.deleteByRequestId(requestId);
                waitAreaMapper.deleteById(requestId);
            } else {  // 没有车了
                requestId = null;
            }

            // 更新数据库：充电区队列、等候区队列，结束充电时间、即下一辆车开始充电时间；充电状态，新来的车充电信息
            chargeRecordMapper.updateEndTime(Long.valueOf(chargeDone.getId()), new Timer().getCurFormatTime());
            chargerMapper.updateCumulativeUsageTimes(Long.valueOf(chargeDone.getChargePileId()),  (chargerMapper.selectCumulativeUsageTimes(Long.valueOf(chargeDone.getChargePileId())) + 1));
            chargerMapper.updateCumulativeChargingTime( chargeDone.getChargePileId(),
                    chargeDone.getChargeTime().add(chargerMapper.selectOne(new QueryWrapper<Charger>().eq("id",chargeDone.getChargePileId())).getCumulativeChargingTime()));
            chargerMapper.updateCumulativeChargingAmount(Long.valueOf(chargeDone.getChargePileId()), String.valueOf(Float.parseFloat(chargerMapper.selectCumulativeChargingAmount(Long.valueOf(chargeDone.getChargePileId()))) + chargeDone.getRequireAmount()));

            chargeRequestMapper.updateState(Long.valueOf(chargeDone.getId()), 0);
            chargeRequestMapper.updateChargePileId(requestId, Long.valueOf(pileId));

            if (requestId != null) {
//                ChargeArea chargeArea = new ChargeArea(pileId, String.valueOf(requestId));
//                chargeAreaMapper.insert(chargeArea);
//                waitAreaMapper.deleteById(requestId);
//                waitQueueMapper.
                ChargeArea chargeArea = new ChargeArea(pileId, String.valueOf(requestId));
                chargeAreaMapper.insert(chargeArea);
                waitAreaMapper.delete(new QueryWrapper<WaitArea>().eq("request_id", requestId));
                Long chargeId = Long.valueOf(chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                        .eq("id", requestId)).getChargeId());
                waitQueueMapper.delete(new QueryWrapper<WaitQueue>().eq("charge_id", chargeId));
                chargeWaitAreaMapper.delete(new QueryWrapper<ChargeWaitArea>().eq("request_id", requestId));
                if (requestId.equals(chargeList.get(0))) {
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("state", 3)); // 正在充电
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("start_time", new Timer().getCurTimestamp()));
                } else {
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("state", 2)); // 充电区等待
                }

            }

            Long firstReqId = chargeList.isEmpty() ? null : Long.valueOf(chargeList.get(0));
            if (firstReqId != null) {
                ChargeRequest req = chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                        .eq("id", firstReqId));
                Integer reqState = req.getState();
                if (reqState == 1 || reqState == 2 || reqState == 4 || reqState == 5) {
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", firstReqId).set("state", 3)); // 正在充电
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", firstReqId).set("start_time", new Timer().getCurTimestamp()));
                }
            }


        }
        else if (scheduleType == 2) {

            Integer curState = chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                    .eq("id", requestId)).getState();
            if (curState == 3 && !mustSch) {
                return;
            }
//            String chargeMode = chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
//                    .eq("id", requestId)).getChargeMode();
//            String pileId = Long.valueOf(chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
//                    .eq("id", requestId)).getChargePileId());
            ChargeRequest last = chargeRequestMapper.getChargeRequestById(requestId);
            if (last !=null){
                String chargeMode = last.getChargeMode();
                String pileId = last.getChargePileId();

                List<String> chargeList = chargeAreaMapper.selectBypileId(pileId);
                List<String> chargeWaitList = chargeWaitAreaMapper.selectChargeRequestIdsByChargeMode(chargeMode);
                List<String> waitList = waitAreaMapper.selectWaitRequestIdsByChargeMode(chargeMode);



                List<Long> endTimeList = new ArrayList<>();
                List<Long> theirPileIdList = new ArrayList<>();

                if (chargeMode.equals("F")) {
                    //fastChargers为0后续就不执行了
                    List<Charger> fastChargers = chargerMapper.selectList(new QueryWrapper<Charger>()
                            .eq("type", "F")
                            .eq("charger_status", "RUNNING"));
                    for (Charger charger : fastChargers) {
                        Long nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                                .eq("pile_id", charger.getId()));
                        charger.setLastEndTime(nowQueueLen != 0 ? charger.getLastEndTime() : new Timer().getCurTimestamp());
                        chargerMapper.updateById(charger);
                        nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                                .eq("pile_id", charger.getId()));
                        int M = charger.getChargingQueueLen();
                        if (nowQueueLen < M) {
                            endTimeList.add(charger.getLastEndTime());
                            theirPileIdList.add(Long.valueOf(charger.getId()));
                        }
                    }
                } else { // T-慢充
                    List<Charger> slowChargers = chargerMapper.selectList(new QueryWrapper<Charger>()
                            .eq("type", "T")
                            .eq("charger_status", "RUNNING"));
                    for (Charger charger : slowChargers) {
                        Long nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                                .eq("pile_id", charger.getId()));
                        charger.setLastEndTime(nowQueueLen != 0 ? charger.getLastEndTime() : new Timer().getCurTimestamp());
                        chargerMapper.updateById(charger);
                        nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                                .eq("pile_id", charger.getId()));
                        int M = charger.getChargingQueueLen();
                        if (nowQueueLen < M) {
                            endTimeList.add(charger.getLastEndTime());
                            theirPileIdList.add(Long.valueOf(charger.getId()));
                        }
                    }
                }

                if (endTimeList.isEmpty()) {
                    if (!error) {
                        return;
                    } else {
                        chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                                .eq("id", requestId).set("state", 5));
                        return;
                    }
                }

                pileId = String.valueOf(theirPileIdList.get(endTimeList.indexOf(Collections.min(endTimeList))));

                Long nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                        .eq("pile_id", pileId));
                chargerMapper.update(null, new UpdateWrapper<Charger>()
                        .eq("id", pileId).set("last_end_time", nowQueueLen != 0 ? chargerMapper.selectById(pileId).getLastEndTime() :
                                chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                                        .eq("id", requestId)).getChargeTime().add(BigDecimal.valueOf(new Timer().getCurTimestamp()))));

                if (chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>().eq("request_id", requestId)) == 0) {
                    ChargeArea chargeArea = new ChargeArea(pileId, String.valueOf(requestId));
                    chargeAreaMapper.insert(chargeArea);
                } else {
                    chargeAreaMapper.update(null, new UpdateWrapper<ChargeArea>()
                            .eq("request_id", requestId).set("pile_id", pileId));
                }

                waitAreaMapper.delete(new QueryWrapper<WaitArea>().eq("request_id", requestId));
                waitQueueMapper.delete(new QueryWrapper<WaitQueue>()
                        .eq("charge_id", chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                                .eq("id", requestId)).getChargeId()));
                chargeWaitAreaMapper.delete(new QueryWrapper<ChargeWaitArea>()
                        .eq("request_id", requestId));

                chargeList = chargeAreaMapper.selectList(new QueryWrapper<ChargeArea>()
                                .eq("pile_id", pileId)).stream()
                        .map(ChargeArea::getRequestId)
                        .collect(Collectors.toList());

                if (String.valueOf(requestId).equals(String.valueOf(chargeList.get(0))) &&
                        chargeRequestMapper.selectOne(new QueryWrapper<ChargeRequest>()
                                .eq("charge_pile_id", pileId)
                                .eq("state", 3)) == null) {
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("state", 3));
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("start_time", new Timer().getCurTimestamp()));
                } else {
                    chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                            .eq("id", requestId).set("state", error ? 5 : 2));
                }

                chargeRequestMapper.update(null, new UpdateWrapper<ChargeRequest>()
                        .eq("id", requestId).set("charge_pile_id", pileId));
            }
            else System.out.println("getChargeRequestByUserId(requestId) WRONG");

        }
        else if (scheduleType == 3) {
            List<String> chargeWaitList = chargeWaitAreaMapper.selectList(new QueryWrapper<ChargeWaitArea>()
                            .eq("type", type)).stream()
                    .map(ChargeWaitArea::getRequestId)
                    .collect(Collectors.toList());

            boolean scheduling = true;
            List<Charger> chargers = chargerMapper.selectList(new QueryWrapper<Charger>()
                    .eq("type", type)
                    .eq("charger_status", "RUNNING"));
            for (Charger charger : chargers) {
                Long nowQueueLen = chargeAreaMapper.selectCount(new QueryWrapper<ChargeArea>()
                        .eq("pile_id", charger.getId()));
                if (nowQueueLen > 0) {
                    List<String> tmpList = chargeAreaMapper.selectList(new QueryWrapper<ChargeArea>()
                                    .eq("pile_id", charger.getId())).stream()
                            .map(ChargeArea::getRequestId)
                            .collect(Collectors.toList());
                    chargeWaitList.addAll(tmpList);
                }
            }
            chargeWaitList.sort(Comparator.naturalOrder());

            while (!chargeWaitList.isEmpty()) {
                requestId = Long.valueOf(chargeWaitList.get(0));
                schedule(2, requestId,null,null,false,false);
                chargeWaitList.remove(0);
                scheduling = false;
            }

            List<Long> carInWaitArea =waitAreaMapper.getCarInWaitAreaOrderByAsc(type);
//                    waitAreaMapper.selectList(new QueryWrapper<WaitArea>()
//                            .eq("type", type))
//                    .orderBy("request_id")
//                    .stream()
//                    .map(WaitArea::getRequestId)
//                    .collect(Collectors.toList());

            for (Long requestIds : carInWaitArea) {
                schedule(2, requestIds,null,null,false,false);
            }
        } else if (scheduleType ==4) {
            
            List<ChargeRequest> chargeDoneList = chargeRequestMapper.selectList(new QueryWrapper<ChargeRequest>()
                    .in("state", Arrays.asList(2, 3, 5))
                    .eq("charge_pile_id", errChargerId));

            if (chargeDoneList == null || chargeDoneList.isEmpty()) {
                return;
            }

            for (ChargeRequest chargeDone : chargeDoneList) {
                chargeWaitAreaMapper.insert(new ChargeWaitArea(chargeDone.getId(), chargeDone.getChargeMode()));
                chargeAreaMapper.delete(new QueryWrapper<ChargeArea>().eq("request_id", chargeDone.getId()));
            }

            List<String> chargeWaitList = chargeWaitAreaMapper.selectList(new QueryWrapper<ChargeWaitArea>()
                            .eq("type", chargeDoneList.get(0).getChargeMode()))
                    .stream()
                    .map(ChargeWaitArea::getRequestId)
                    .collect(Collectors.toList());

            for (String requestIdS : chargeWaitList) {
                schedule(2, Long.valueOf(requestIdS),null,null, true, true);
            }

        }


    }



}